package cn.brondfish.icedfish.framework.util;

import android.content.Context;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.spec.MGF1ParameterSpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import cn.brondfish.icedfish.framework.exception.BF_SecretException;

public class BF_SecretUtil {
    /** * RSA最大加密块大小 */
    private static final int MAX_ENCRYPT_BLOCK = 245;
    /** * RSA最大解密块大小 */
    private static final int MAX_DECRYPT_BLOCK = 256;

    private final int KEY_SIZE = 2048;
    // 默认别名
    private final static String DEFAULT_ALIAS = "iced_fish";
    // 默认加密模式
    private final static String DEFAULT_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";

    private static volatile BF_SecretUtil secretUtil;
    private Context app_context;

    public BF_SecretUtil(Context context) {
        this.app_context = context;
    }

    public byte[] decryptData(byte[] value) throws BF_SecretException {
        try {
            Cipher cipher = Cipher.getInstance(DEFAULT_TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE,getPrivateKey(),new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
            int inputLen = value.length;
            int offLen = 0;
            int i = 0;
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            while(inputLen - offLen > 0){
                byte[] cache;
                if(inputLen - offLen > MAX_DECRYPT_BLOCK){
                    cache = cipher.doFinal(value,offLen,MAX_DECRYPT_BLOCK);
                }else{
                    cache = cipher.doFinal(value,offLen,inputLen - offLen);
                }
                byteArrayOutputStream.write(cache);
                i++;
                offLen = MAX_DECRYPT_BLOCK * i;
            }
            byteArrayOutputStream.close();
            byte[] byteArray = byteArrayOutputStream.toByteArray();
            return byteArray;
        } catch (NoSuchPaddingException e) {
            throw new BF_SecretException(e);
        } catch (IllegalBlockSizeException e) {
            throw new BF_SecretException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new BF_SecretException(e);
        } catch (BadPaddingException e) {
            throw new BF_SecretException(e);
        } catch (InvalidKeyException e) {
            throw new BF_SecretException(e);
        } catch (IOException e) {
            throw new BF_SecretException(e);
        } catch (InvalidAlgorithmParameterException e) {
            throw new BF_SecretException(e);
        }
    }

    public byte[] encryptData(byte[] value) throws BF_SecretException{
        try {
            Cipher cipher = Cipher.getInstance(DEFAULT_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE,getPublicKey(),new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
            int inputLen = value.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(value, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(value, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = out.toByteArray();
            out.close();
            return encryptedData;
        } catch (NoSuchPaddingException e) {
            throw new BF_SecretException(e);
        } catch (IllegalBlockSizeException e) {
            throw new BF_SecretException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new BF_SecretException(e);
        } catch (BadPaddingException e) {
            throw new BF_SecretException(e);
        } catch (InvalidKeyException e) {
            throw new BF_SecretException(e);
        } catch (IOException e) {
            throw new BF_SecretException(e);
        } catch (InvalidAlgorithmParameterException e) {
            throw new BF_SecretException(e);
        }
    }

    public static BF_SecretUtil getInstance(Context context){
        if (secretUtil == null){
            synchronized(BF_SecretUtil.class){
                if(secretUtil == null) {
                    secretUtil = new BF_SecretUtil(context);
                }
            }
        }
        return secretUtil;
    }

    public PublicKey getPublicKey() throws BF_SecretException{
        try {
            // 初始化 KeyPairGenerator
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            if(!keyStore.containsAlias(DEFAULT_ALIAS)) {
                genKeyStoreFile();
            }
            Certificate cer = keyStore.getCertificate(DEFAULT_ALIAS);
            return cer.getPublicKey();
        } catch (KeyStoreException e) {
            throw new BF_SecretException(e);
        } catch (CertificateException e) {
            throw new BF_SecretException(e);
        } catch (IOException e) {
            throw new BF_SecretException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new BF_SecretException(e);
        }
    }

    public PrivateKey getPrivateKey() throws BF_SecretException{
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            if(!keyStore.containsAlias(DEFAULT_ALIAS)) {
                genKeyStoreFile();
            }
            Key key = keyStore.getKey(DEFAULT_ALIAS,null);
            if (key instanceof PrivateKey) {
                return (PrivateKey) key;
            } else {
                throw new BF_SecretException("The key is not a private key");
            }
        } catch (UnrecoverableKeyException e) {
            throw new BF_SecretException(e);
        } catch (CertificateException e) {
            throw new BF_SecretException(e);
        } catch (KeyStoreException e) {
            throw new BF_SecretException(e);
        } catch (IOException e) {
            throw new BF_SecretException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new BF_SecretException(e);
        }
    }

    public void genKeyStoreFile() throws BF_SecretException{
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            if(!keyStore.containsAlias(DEFAULT_ALIAS)) {
                // 初始化 KeyPairGenerator
                KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
                        KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");

                // 设置密钥生成参数
                keyPairGenerator.initialize(
                        new KeyGenParameterSpec.Builder(DEFAULT_ALIAS,
                                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY |
                                        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                                .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
                                .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
                                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
                                .setKeySize(KEY_SIZE) // 设置RSA密钥长度
                                .build());
                keyPairGenerator.generateKeyPair();
            }
        } catch (NoSuchAlgorithmException e) {
            throw new BF_SecretException(e);
        } catch (InvalidAlgorithmParameterException e) {
            throw new BF_SecretException(e);
        } catch (NoSuchProviderException e) {
            throw new BF_SecretException(e);
        } catch (CertificateException e) {
            throw new BF_SecretException(e);
        } catch (KeyStoreException e) {
            throw new BF_SecretException(e);
        } catch (IOException e) {
            throw new BF_SecretException(e);
        }
    }

}
